package l2ft.gameserver.model.entity.events.impl;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;

import javolution.util.FastMap;
import l2ft.commons.collections.MultiValueSet;
import l2ft.commons.threading.RunnableImpl;
import l2ft.commons.util.Rnd;
import l2ft.gameserver.ThreadPoolManager;
import l2ft.gameserver.data.xml.holder.FightClubMapHolder;
import l2ft.gameserver.data.xml.holder.InstantZoneHolder;
import l2ft.gameserver.handler.bbs.CommunityBoardManager;
import l2ft.gameserver.instancemanager.ReflectionManager;
import l2ft.gameserver.listener.actor.player.OnPlayerExitListener;
import l2ft.gameserver.listener.zone.OnZoneEnterLeaveListener;
import l2ft.gameserver.model.Creature;
import l2ft.gameserver.model.Effect;
import l2ft.gameserver.model.GameObject;
import l2ft.gameserver.model.GameObjectsStorage;
import l2ft.gameserver.model.Party;
import l2ft.gameserver.model.Playable;
import l2ft.gameserver.model.Player;
import l2ft.gameserver.model.SimpleSpawner;
import l2ft.gameserver.model.Skill;
import l2ft.gameserver.model.World;
import l2ft.gameserver.model.Zone;
import l2ft.gameserver.model.base.InvisibleType;
import l2ft.gameserver.model.base.Race;
import l2ft.gameserver.model.base.RestartType;
import l2ft.gameserver.model.entity.Reflection;
import l2ft.gameserver.model.entity.events.GlobalEvent;
import l2ft.gameserver.model.entity.events.fightclubmanager.FightClubAchievements;
import l2ft.gameserver.model.entity.events.fightclubmanager.FightClubAchievements.ACHIEVEMENT_ACTION;
import l2ft.gameserver.model.entity.events.fightclubmanager.FightClubAchievements.FightClubAchievement;
import l2ft.gameserver.model.entity.events.fightclubmanager.FightClubEventManager;
import l2ft.gameserver.model.entity.events.fightclubmanager.FightClubEventManager.CLASSES;
import l2ft.gameserver.model.entity.events.fightclubmanager.FightClubGameRoom;
import l2ft.gameserver.model.entity.events.fightclubmanager.FightClubGameRoom.FightClubRoomParty;
import l2ft.gameserver.model.entity.events.fightclubmanager.FightClubGameRoom.FightClubRoomTeam;
import l2ft.gameserver.model.entity.events.fightclubmanager.FightClubLastStatsManager;
import l2ft.gameserver.model.entity.events.fightclubmanager.FightClubLastStatsManager.FightClubStatType;
import l2ft.gameserver.model.entity.events.fightclubmanager.FightClubMap;
import l2ft.gameserver.model.entity.events.fightclubmanager.FightClubPlayer;
import l2ft.gameserver.model.entity.events.fightclubmanager.FightClubTeam;
import l2ft.gameserver.model.entity.league.League;
import l2ft.gameserver.model.instances.BufferInstance;
import l2ft.gameserver.model.instances.NpcInstance;
import l2ft.gameserver.network.l2.components.ChatType;
import l2ft.gameserver.network.l2.components.SystemMsg;
import l2ft.gameserver.network.l2.s2c.Earthquake;
import l2ft.gameserver.network.l2.s2c.ExPVPMatchCCRecord;
import l2ft.gameserver.network.l2.s2c.ExPVPMatchCCRetire;
import l2ft.gameserver.network.l2.s2c.ExShowScreenMessage;
import l2ft.gameserver.network.l2.s2c.ExShowScreenMessage.ScreenMessageAlign;
import l2ft.gameserver.network.l2.s2c.HideCommunityBoard;
import l2ft.gameserver.network.l2.s2c.RelationChanged;
import l2ft.gameserver.network.l2.s2c.Say2;
import l2ft.gameserver.network.l2.s2c.TutorialShowQuestionMark;
import l2ft.gameserver.skills.AbnormalEffect;
import l2ft.gameserver.templates.DoorTemplate;
import l2ft.gameserver.templates.InstantZone;
import l2ft.gameserver.templates.ZoneTemplate;
import l2ft.gameserver.utils.Location;
import l2ft.gameserver.utils.Util;

import org.apache.commons.lang3.reflect.MethodUtils;
import org.napile.primitive.maps.IntObjectMap;
import org.napile.primitive.maps.impl.HashIntObjectMap;

public abstract class AbstractFightClub extends GlobalEvent
{
	/**
	 * TODO
	 * Na Koncu:
	 * zapisywanie wszystkiego co minute
	 * ogarnac LOGGED_OUT_PLAYERS
	 * CHECK shutDown()
	 * sprawdzic wszystkie achievementy
	 * Zbalansowac to ile healerzy dostaja badgesow
	 * configs
	 * obserwacje
	 * logi dotyczace tego ile i jak kto dostal badgesow
	 * Nowy item, flaga. Bez slow down passiva
	 * 
	 * Wczesniej:
	 * Wybor mapy
	 * badge item
	 * 
	 * 
	 * Na Teraz:
	 * Sprawdz real name:x w consoli -W My Statistics zamiast Kill <Player> jest Kill - Teraz jest Kill Player
	 * 
	 * CHECK co sie stanie jesli kolesiowi odemiemy 5 badgesow, a on nie ma zadnego
	 * CHECK afk
	 * CHECK czy postac ktora wyszla z eventu, jest widoczna przez normalnych graczy
	 * CHECK total event time, chyba jest zly
	 * CHECK Afk time jest zly
	 * gdy jestem martwy i czekam na tp, nie mam co zrobic zeby pokazac NIE AFK
	 * czasami widze TO VILLAGE gdy dam LEAVE EVENT
	 * 
	 * CHECK - sprawdzic czy teraz deaths sie od razu updatuje dla innych graczy, jesli tak, zmienic broadcast userinfo na wszystkie eventy
	 * 
	 * Event powinien sie skonczyc o 19:58
	 * 
	 * 
	 * 
	 * Packi: 75% na Vesper 25% na vorpal
	 * W nie instancjonowanych eventach, lider klanu chcach isc na siege/fort moze stworzyc room ilus osobowy ktory bedzie pomagal. Placi za ilosc osob.
	 * W bossach serwer robi rooma ktory przeszkadza atakujacym
	 * 
	 */
	private class ExitListener implements OnPlayerExitListener
	{
		@Override
		public void onPlayerExit(Player player)
		{
			loggedOut(player);
		}
	}
	
	private class ZoneListener implements OnZoneEnterLeaveListener
	{
		@Override
		public void onZoneEnter(Zone zone, Creature actor)
		{
			if(actor.isPlayer())
			{
				FightClubPlayer fPlayer = getFightClubPlayer(actor);
				if(fPlayer != null)
				{
					actor.sendPacket(new Earthquake(actor.getLoc(), 0, 1));
					_leftZone.remove(getFightClubPlayer(actor));
				}
			}
		}

		@Override
		public void onZoneLeave(Zone zone, Creature actor)
		{
			System.out.println("on zone leave");
			if(actor.isPlayer() && _state != EVENT_STATE.NOT_ACTIVE)
			{
				FightClubPlayer fPlayer = getFightClubPlayer(actor);
				System.out.println("player:"+fPlayer);
				if(fPlayer != null)
					_leftZone.put(getFightClubPlayer(actor), zone);
			}
		}
	}
	
	
	public static enum EVENT_STATE {
		NOT_ACTIVE,
		COUNT_DOWN,
		PREPARATION,
		STARTED,
		OVER
	}

	public static final String REGISTERED_PLAYERS = "registered_players";
	public static final String LOGGED_OFF_PLAYERS = "logged_off_players";
	public static final String FIGHTING_PLAYERS = "fighting_players";
	

	public static final int INSTANT_ZONE_ID = 400;
	private static final int CLOSE_LOCATIONS_VALUE = 80;//Used for spawning players
	private static int LAST_OBJECT_ID = 1;//Used for event object id
	
	private static final double BADGES_PER_MINUTE_ON_EVENT = 0.5;
	private static final int BADGES_FOR_MEMBER_OF_WINNING_TEAM = 5;
	private static final int BADGES_FOR_MINUTE_OF_AFK = -1;
	
	private static final int TIME_FIRST_TELEPORT = 60;//in seconds
	private static final int TIME_PLAYER_TELEPORTING = 5;//in seconds
	private static final int TIME_PREPARATION_BEFORE_FIRST_ROUND = 60;//in seconds
	private static final int TIME_PREPARATION_BETWEEN_NEXT_ROUNDS = 30;//in seconds - usable if there are more than 1 round
	private static final int TIME_AFTER_ROUND_END_TO_RETURN_SPAWN = 15;//in seconds - usable if there are more than 1 round
	private static final int TIME_TELEPORT_BACK_TOWN = 30;//in seconds
	private static final int TIME_BEFORE_GIVING_ACHIEVEMENT = 30;//in seconds
	private static final int TIME_MAX_SECONDS_OUTSIDE_ZONE = 10;
	private static final int TIME_TO_BE_AFK = 30;
	
	private static final String[] ROUND_NUMBER_IN_STRING = {"", "1st", "2nd", "3rd", "4th", "5th","6th", "7th", "8th", "9th", "10th"};
	
	// Properties
	private final int _objId;
	private final String _desc;
	private final int _roundRunTime;//in minutes
	private final boolean _teamed;
	private final boolean _buffer;
	private final boolean _rootBetweenRounds;
	private final CLASSES[] _excludedClasses;
	private final int[] _excludedSkills;
	private final boolean _roundEvent;
	private final int _rounds;
	private final int _respawnTime;//in seconds
	private final boolean _ressAllowed;
	private final boolean _instanced;
	private final boolean _showPersonality;
	//Badges
	private final double _badgesKillPlayer;
	private final double _badgesKillPet;
	private final double _badgesDie;
	
	
	//Event variables
	private EVENT_STATE _state = EVENT_STATE.NOT_ACTIVE;
	private ExitListener _exitListener = new ExitListener();
	private ZoneListener _zoneListener = new ZoneListener();
	private FightClubMap _map;
	private Reflection _reflection;
	private List<FightClubTeam> _teams = new ArrayList<>();
	private Map<FightClubPlayer, Zone> _leftZone = new ConcurrentHashMap<>();
	private int _currentRound = 0;
	private boolean _dontLetAnyoneIn = false;
	private FightClubGameRoom _room;
	
	//For duplicating events:
	private MultiValueSet<String> _set;
	
	//Scores
	private Map<String, Integer> _scores = new ConcurrentHashMap<>();
	private Map<String, Integer> _bestScores = new ConcurrentHashMap<>();
	private boolean _scoredUpdated = true;
	//Achievements
	private Map<FightClubPlayer, Long> _achievementWaitMap = new ConcurrentHashMap<>();
	private boolean _achievementsThreadIsActive = false;
	
	//Before event start
	private ScheduledFuture<?> _timer;

	public AbstractFightClub(MultiValueSet<String> set)
	{
		super(set);
		_objId = LAST_OBJECT_ID++;
		_desc = set.getString("desc");
		_roundRunTime = set.getInteger("roundRunTime", -1);
		_teamed = set.getBool("teamed");
		_buffer = set.getBool("buffer");
		_rootBetweenRounds = set.getBool("rootBetweenRounds");
		_excludedClasses = parseExcludedClasses(set.getString("excludedClasses", ""));
		_excludedSkills = parseExcludedSkills(set.getString("excludedSkills", null));
		_roundEvent = set.getBool("roundEvent");
		_rounds = set.getInteger("rounds", -1);
		_respawnTime = set.getInteger("respawnTime");
		_ressAllowed = set.getBool("ressAllowed");
		_instanced = set.getBool("instanced", true);
		_showPersonality = set.getBool("showPersonality", true);
		//Badges
		_badgesKillPlayer = set.getDouble("badgesKillPlayer", 0);
		_badgesKillPet = set.getDouble("badgesKillPet", 0);
		_badgesDie = set.getDouble("badgesDie", 0);
		
		_set = set;
	}

	public void prepareEvent()
	{
		System.out.println("prepareEvent");
		chooseMap();
	}

	@Override
	public void startEvent()
	{
		super.startEvent();
		
		System.out.println("startEvent");
		_state = EVENT_STATE.PREPARATION;
		
		//Getting all zones
		IntObjectMap<DoorTemplate> doors = new HashIntObjectMap<>(0);
		Map<String, ZoneTemplate> zones = new HashMap<>();
		for(Entry<Integer, Map<String, ZoneTemplate>> entry : getMap().getTerritories().entrySet())
			for(Entry<String, ZoneTemplate> team : entry.getValue().entrySet())
				zones.put(team.getKey(), team.getValue());
		//Creating reflection if needed
		if(isInstanced())
			createReflection(doors, zones);
		
		for(Zone zone : getReflection().getZones())
			System.out.println("zone in reflection:"+zone.getName());
		
		List<FightClubPlayer> playersToRemove = new ArrayList<>();
		for(FightClubPlayer iFPlayer : getPlayers(REGISTERED_PLAYERS))
		{
			stopInvisibility(iFPlayer.getPlayer());
			if(!checkIfRegisteredPlayerMeetCriteria(iFPlayer))
				playersToRemove.add(iFPlayer);
			iFPlayer.getPlayer().sendPacket(new HideCommunityBoard());
			
			if(isHidePersonality())
				iFPlayer.getPlayer().setPolyId(FightClubGameRoom.getPlayerClassGroup(iFPlayer.getPlayer()).getTransformId());
		}
		
		for(FightClubPlayer playerToRemove : playersToRemove)
			unregister(playerToRemove.getPlayer());
		
		if(isTeamed())
		{
			spreadIntoTeamsAndPartys();
		}
		
		teleportRegisteredPlayers();
		
		updateEveryScore();
		
		//Deleting room
		FightClubEventManager.getInstance().removeRoom(_room);
		_room.quietlyDestroy();
		_room = null;
		for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS, REGISTERED_PLAYERS))
		{
			iFPlayer.getPlayer().setFightClubGameRoom(null);
		}
		
		startNewTimer(true, TIME_PLAYER_TELEPORTING*1000, "startRoundTimer", TIME_PREPARATION_BEFORE_FIRST_ROUND);
		
		ThreadPoolManager.getInstance().schedule(new LeftZoneThread(), 5000L);
	}
	
	public void startRound()
	{
		System.out.println("startRound");
		_state = EVENT_STATE.STARTED;
		
		_currentRound++;
		
		if(isRoundEvent())
			if(_currentRound == _rounds)
				sendMessageToFighting(MESSAGE_TYPES.SCREEN_BIG, "Last Round STARTED!", true);
			else
				sendMessageToFighting(MESSAGE_TYPES.SCREEN_BIG, "Round "+_currentRound+" STARTED!", true);
		else
			sendMessageToFighting(MESSAGE_TYPES.SCREEN_BIG, "Fight!", true);
		
		unrootPlayers();

		if(getRoundRuntime() > 0)
		{
			startNewTimer(true, (int)((double)getRoundRuntime()/2*60000), "endRoundTimer", (int)((double)getRoundRuntime()/2*60));
		}
		
		if(_currentRound == 1)
		{
			ThreadPoolManager.getInstance().schedule(new TimeSpentOnEventThread(), 10*1000);
			ThreadPoolManager.getInstance().schedule(new CheckAfkThread(), 1000);
			waitAndStartAchievements();
		}
		
		for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
		{
			hideScores(iFPlayer.getPlayer());
			iFPlayer.getPlayer().broadcastUserInfo(true);
		}
	}
	
	public void endRound()
	{
		System.out.println("endRound");
		_state = EVENT_STATE.OVER;

		if(!isLastRound())
			sendMessageToFighting(MESSAGE_TYPES.SCREEN_BIG, "Round "+_currentRound+" is over!", false);
		else
			sendMessageToFighting(MESSAGE_TYPES.SCREEN_BIG, "Event is now Over!", false);

		ressAndHealPlayers();
		
		for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
			showScores(iFPlayer.getPlayer());
		
		if(!isLastRound())
		{
			//Changing team spawn location
			if(isTeamed())
			{
				for(FightClubTeam team : getTeams())
					team.setSpawnLoc(null);
			}
			
			ThreadPoolManager.getInstance().schedule(new Runnable()
			{
				@Override
				public void run()
				{
					for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
					{
						teleportSinglePlayer(iFPlayer, false, true);
					}
					
					startNewTimer(true, 0, "startRoundTimer", TIME_PREPARATION_BETWEEN_NEXT_ROUNDS);
				}
			}, TIME_AFTER_ROUND_END_TO_RETURN_SPAWN*1000);
		}
		else
		{
			ThreadPoolManager.getInstance().schedule(new Runnable() {
				@Override
				public void run()
				{
					stopEvent();
				}
			}, 10*1000);
			
			if(isTeamed())
				announceWinnerTeam(true, null);
			else
				announceWinnerPlayer(true, null);
		}
		
		for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
			iFPlayer.getPlayer().broadcastUserInfo(true);
	}
	

	@Override
	public void stopEvent()
	{
		System.out.println("stopEvent");
		_state = EVENT_STATE.NOT_ACTIVE;
		super.stopEvent();
		reCalcNextTime(false);
		
		giveRewards();
		
		FightClubLastStatsManager.getInstance(this).finishEvent();

		for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
		{
			iFPlayer.getPlayer().getInvidualAbnormals().clear();
			if(iFPlayer.getAchProgress() != null)
			{
				FightClubAchievements.getInstance().addAchievementToStats(iFPlayer, false);
				iFPlayer.achievementFailed();
				sendMessageToPlayer(iFPlayer, MESSAGE_TYPES.SCREEN_SMALL, "");
			}
			iFPlayer.getPlayer().broadcastCharInfo();
			if(iFPlayer.getPlayer().getPet() != null)
				iFPlayer.getPlayer().getPet().broadcastCharInfo();
		}
		
		ThreadPoolManager.getInstance().schedule(new Runnable() {
			
			@Override
			public void run()
			{
				for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
				{
					CommunityBoardManager.getInstance().getCommunityHandler("_fightClubShop_").onBypassCommand(iFPlayer.getPlayer(), "_fightClubShop_1_1");
				}

			}
		}, 10*1000);
		
		ThreadPoolManager.getInstance().schedule(new Runnable() {
			
			@Override
			public void run()
			{
				for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
				{
					//Hiding top players scores
					hideScores(iFPlayer.getPlayer());
					//Showing tutorial main page
					FightClubEventManager.getInstance().sendEventPlayerMenu(iFPlayer.getPlayer());
				}
			}
		}, 30*1000);
		
		//TODO while setting event NOT_ACTIVE - set login location of all LOGGED_OUT_PLAYERS to giran, give them points as reward
	}

	public void shutDown(int secondsLeft)
	{
		startNewTimer(false, 0, "shutDownTimer", secondsLeft);
	}

	public void stopFirstCountDown()
	{
		System.out.println("state:"+_state);
		if(_state == EVENT_STATE.COUNT_DOWN)
			_timer.cancel(true);
	}
	
	public void stopEventByAdmin()
	{
		_timer.cancel(true);
		stopEvent();
	}
	
	public void destroyMe()
	{
		if(getReflection() != null)
		{
			for(Zone zone : getReflection().getZones())
				zone.removeListener(_zoneListener);
			getReflection().collapse();
		}
		if(_timer != null)
			_timer.cancel(false);
		_timer = null;
		_achievementWaitMap.clear();
		_bestScores.clear();
		_scores.clear();
		_leftZone.clear();
		getObjects().clear();
		_set = null;
		_room = null;
		_zoneListener = null;
		for(Player player : GameObjectsStorage.getAllPlayersForIterate())
			player.removeListener(_exitListener);
		_exitListener = null;
	}
	

	//===============================================================================================================
	//										Unique event related actions
	//===============================================================================================================
	
	/**
	 * Remember that actor may be NULL/player/pet/npc. Victim: player/pet/npc
	 */
	public void onKilled(Creature actor, Creature victim, Skill skill)
	{
		if(victim.isPlayer() && getRespawnTime() > 0)
			showScores(victim);
		
		if(actor != null && actor.isPlayable())
			FightClubAchievements.getInstance().onKilled(actor, victim, skill);
		
		System.out.println("onKilled");
		//Respawn type for non teamed events
		if(victim.isPlayer() && getRespawnTime() > 0 && !_ressAllowed && getFightClubPlayer(victim.getPlayer()) != null)
			startNewTimer(false, 0, "ressurectionTimer", getRespawnTime(), getFightClubPlayer(victim));
	}

	public void requestRespawn(Player activeChar, RestartType restartType)
	{
		if(getRespawnTime() > 0)
			startNewTimer(false, 0, "ressurectionTimer", getRespawnTime(), getFightClubPlayer(activeChar));
	}
	
	@Override
	public boolean canAttack(Creature target, Creature attacker, Skill skill, boolean force)
	{
		if(_state != EVENT_STATE.STARTED)
			return false;
		Player player = attacker.getPlayer();
		if(player == null)
			return true;
		
		if(isTeamed())
		{
			FightClubPlayer targetFPlayer = getFightClubPlayer(target);
			FightClubPlayer attackerFPlayer = getFightClubPlayer(attacker);
			
			if(targetFPlayer.getTeam() == attackerFPlayer.getTeam())
				return false;
		}
		
		if(isInvisible(player, player))
		{
			return false;
		}
			
		return true;
	}
	
	@Override
	public boolean canUseSkill(Creature caster, Creature target, Skill skill)
	{
		System.out.println("trying to use "+skill.getId()+" excluded skills:"+_excludedSkills);
		if(_excludedSkills != null)
		{
			for(int id : _excludedSkills)
				if(skill.getId() == id)
				{
					System.out.println("can use skill? false");
					return false;
				}
		}
		System.out.println("can use skill? true");
		return true;
	}
	
	@Override
	public SystemMsg checkForAttack(Creature target, Creature attacker, Skill skill, boolean force)
	{
		if(!canAttack(target, attacker, skill, force))
			return SystemMsg.INVALID_TARGET;
		return null;
	}

	public boolean canRessurect(Player player, Creature creature, boolean force)
	{
		return _ressAllowed;
	}
	
	/**
	 * @return -1 if it have to be unchanged
	 */
	public int getMySpeed(Player player)
	{
		return -1;
	}

	/**
	 * @return -1 if it have to be unchanged
	 */
	public int getPAtkSpd(Player player)
	{
		return -1;
	}
	
	/**
	 * Removing window that appears after death
	 */
	@Override
	public void checkRestartLocs(Player player, Map<RestartType, Boolean> r)
	{
		r.clear();
		if(isTeamed() && getRespawnTime() > 0 && getFightClubPlayer(player) != null && _ressAllowed)
		{
			r.put(RestartType.TO_FLAG, true);
		}
	}
	
	public boolean canUseBuffer(Player player, boolean heal)
	{
		FightClubPlayer fPlayer = getFightClubPlayer(player);
		if(!getBuffer())
			return false;
		if(player.isInCombat())
			return false;
		if(heal)
		{
			if(player.isDead())
				return false;
			if(_state != EVENT_STATE.STARTED)
				return true;
			if(fPlayer.isInvisible())
				return true;
			return false;
		}
		return true;
	}
	
	public boolean canUsePositiveMagic(Creature user, Creature target)
	{
		Player player = user.getPlayer();
		if(player == null)
			return true;
		
		if(!isFriend(user, target))
			return false;
		
		
		if(isInvisible(player, player))
		{
			return false;
		}
		
		return true;
	}
	
	@Override
	public int getRelation(Player thisPlayer, Player target, int oldRelation)
	{
		if(_state == EVENT_STATE.STARTED)
			return isFriend(thisPlayer, target) ? getFriendRelation() : getWarRelation();
		else
			return oldRelation;
	}
	
	public boolean canJoinParty(Player sender, Player receiver)
	{
		return isFriend(sender, receiver);
	}
	
	/**
	 * Join Clan, Join Ally, Command Channel
	 */
	public boolean canReceiveInvitations(Player sender, Player receiver)
	{
		return true;
	}
	
	public boolean canOpenStore(Player player)
	{
		return false;
	}
	
	public boolean loseBuffsOnDeath(Player player)
	{
		return false;
	}
	
	protected boolean inScreenShowBeScoreNotKills()
	{
		return true;
	}
	
	protected boolean inScreenShowBeTeamNotInvidual()
	{
		return isTeamed();
	}
	
	public boolean isFriend(Creature c1, Creature c2)
	{
		if(c1.equals(c2))
			return true;
		
		if(!c1.isPlayable() || !c2.isPlayable())
		{
			System.out.println("not playable");
			return true;
		}
		
		FightClubPlayer fPlayer1 = getFightClubPlayer(c1.getPlayer());
		FightClubPlayer fPlayer2 = getFightClubPlayer(c2.getPlayer());
		
		if(isTeamed())
		{
			//Different teams
			if(fPlayer1.getTeam() != fPlayer2.getTeam())
			{
				return false;
			}
			//Same team
			else
			{
				return true;
			}
		}
		else
		{
			return false;
		}
	}
	
	public boolean checkAchievementFitsTargetType(FightClubPlayer fPlayer, Creature victim)
	{
		switch(fPlayer.getAchievement().getTargetType())
		{
		case PLAYER:
			return victim.isPlayer();
		case PET:
			return victim.isPlayable();
		case MONSTER:
			return victim.isMonster();
			
		default:
			return false;
		}
	}
	/**
	 * Is there target that can be choosen for achievement?
	 */
	public boolean achievementTargetExists(FightClubAchievement ach, FightClubPlayer fPlayer)
	{
		Object target = getAchievementTarget(ach, fPlayer);
		return target == null || !target.equals("-1");
	}
	
	/**
	 * MAKE achievementTargetExists CHECK BEFORE!!
	 * Adding effect to target
	 */
	protected Object setupAchivementTarget(FightClubAchievement ach, FightClubPlayer fPlayer)
	{
		Object target = getAchievementTarget(ach, fPlayer);
		switch(ach.getTargetRequirements())
		{
		case SPECIFIC:
			Creature cTarget = (Creature)target;
			if(ach.getAction() == ACHIEVEMENT_ACTION.HEAL)
				cTarget.getTellAboutDeathToPlayers().add(fPlayer.getPlayer());
			cTarget.startInvidualAbnormalEffect(AbnormalEffect.S_NAVIT, fPlayer.getPlayer());
			System.out.println("starting invidual effect, player:"+fPlayer.getPlayer().getName()+" target:"+cTarget.getName());
			cTarget.broadcastCharInfo();
			if(cTarget.isPlayable() && cTarget.getPet() != null)
				cTarget.getPet().broadcastCharInfo();
			break;
			default:
				break;
		}
		return target;
	}
	
	public String getAchievementTargetName(FightClubPlayer fPlayer)
	{
		Object target = fPlayer.getTarget();
		switch(fPlayer.getAchievement().getTargetRequirements())
		{
		case LEAGUE:
			League league = (League)target;
			return league.name();
		case RACE:
			Race race = (Race)target;
			return race.name();
		case ANY:
			return null;
		case SPECIFIC:
			Creature cTarget = (Creature)target;
			return cTarget.getName();
		case TEAM:
			FightClubTeam team = (FightClubTeam)target;
			return team.getName();
		}
		return "";
	}
	/**
	 * If ANY - @return null
	 * If specific - @return object
	 * If doesn't exist - @return -1
	 */
	protected Object getAchievementTarget(FightClubAchievement ach, FightClubPlayer fPlayer)
	{
		switch(ach.getTargetRequirements())
		{
		case LEAGUE:
			List<League> leagues = new ArrayList<>();
			for(League league : League.values())
				if(!fPlayer.getPlayer().getLeague().equals(league))
					for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
						if(iFPlayer.getPlayer().getLeague().equals(league))
						{
							leagues.add(league);
							break;
						}
			if(leagues.isEmpty())
				return "-1";
			return Rnd.get(leagues);
		case RACE:
			List<Race> races = new ArrayList<>();
			for(Race race : Race.values())
				if(!fPlayer.getPlayer().getRace().equals(race))
					for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
						if(iFPlayer.getPlayer().getRace().equals(race))
						{
							races.add(race);
							break;
						}
			System.out.println("races to choose:"+races.size());
			if(races.isEmpty())
				return "-1";
			return Rnd.get(races);
		case ANY:
			return null;
		case SPECIFIC:
			Creature target = getSpecificCreatureRequirement(ach, fPlayer);
			if(target == null)
				return "-1";
			return target;
		case TEAM:
			if(isTeamed())
			{
				List<FightClubTeam> possibleFTeams = new ArrayList<>();
				for(FightClubTeam fTeam : _teams)
					if(!fTeam.equals(fPlayer.getTeam()))
						possibleFTeams.add(fTeam);
				
				if(!possibleFTeams.isEmpty())
					Rnd.get(possibleFTeams);
			}
			return "-1";
		}
		return null;
	}
	
	protected Creature getSpecificCreatureRequirement(FightClubAchievement ach, FightClubPlayer fPlayer)
	{
		switch(ach.getTargetType())
		{
		case PET:
			List<FightClubPlayer> allPossibleTargets = new ArrayList<>();
			if(isTeamed())
			{
				for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
					if(iFPlayer.getTeam() != fPlayer.getTeam() && iFPlayer.getPlayer().getPet() != null)
						allPossibleTargets.add(iFPlayer);
			}
			else
			{
				for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
					if(iFPlayer.getPlayer().getPet() != null && !iFPlayer.equals(fPlayer))
						allPossibleTargets.add(iFPlayer);
			}
			
			if(allPossibleTargets.isEmpty())
				return null;
			return Rnd.get(allPossibleTargets).getPlayer();
		case PLAYER:
			allPossibleTargets = new ArrayList<>();
			
			boolean isHeal = ach.getAction() == ACHIEVEMENT_ACTION.HEAL;
			
			if(isTeamed())
			{
				for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
					if(iFPlayer.getTeam() != fPlayer.getTeam() && (!isHeal || iFPlayer.getPlayer().getCurrentHpPercents() < 50))
						allPossibleTargets.add(iFPlayer);
			}
			else
			{
				for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
					if(!iFPlayer.equals(fPlayer) && (!isHeal || iFPlayer.getPlayer().getCurrentHpPercents() < 50))
						allPossibleTargets.add(iFPlayer);
			}
			if(allPossibleTargets.isEmpty())
				return null;
			return Rnd.get(allPossibleTargets).getPlayer();
			
		default:
			return null;
		}
	}
	
	//TODO How to check, if player can have Spirit of shilen effect 3? ATM its checking if player has Spirit of Shilen skill in skill bar
	public Object getActionForAchievement(FightClubAchievement ach, FightClubPlayer fPlayer)
	{
		int[] skillIds = getSkillIdsForAction(ach);
		if(skillIds == null)
			return null;
		
		List<Skill> skillsThatPlayerHas = new ArrayList<>();
		
		for(Skill skill : fPlayer.getPlayer().getAllSkills())
			for(int id : skillIds)
				if(id == skill.getId())
					skillsThatPlayerHas.add(skill);
		
		if(skillsThatPlayerHas.isEmpty())
			return null;
		
		return Rnd.get(skillsThatPlayerHas);
	}
	
	private int[] getSkillIdsForAction(FightClubAchievement ach)
	{
		switch(ach.getAction())//TODO fix those skills later. Make those tables FINAL and add them above the method
		{
			case BUFF:
				//Cleanse
				return new int[] {1409};
			case HEAL:
				//All advanced healing skills
				return new int[] {1217, 1218, 1401, 1553};
			case KILL_AND_HAVE_EFFECT:
				//Improved Condition
				return new int[] {1501};
			case KILL_WITH_SKILL:
				//Prominence
				return new int[] {1230};
			case LAND_EFFECT:
				//Mass Block Shield
				return new int[] {1360};
			default:
				return null;
		}
	}
	
	public boolean isInvisible(Player actor, Player watcher)
	{
		//if(watcher.isGM())
		//	return false;
		
		if(actor.getInvisibleType() == InvisibleType.NORMAL)
			return true;
		
		return false;
	}
	
	public String getVisibleName(Player player, String currentName, boolean toMe)
	{
		if(isHidePersonality() && !toMe)
			return "Player";
		return currentName;
	}
	
	public String getVisibleTitle(Player player, String currentTitle, boolean toMe)
	{
		return currentTitle;
	}
	
	public int getVisibleTitleColor(Player player, int currentTitleColor, boolean toMe)
	{
		return currentTitleColor;
	}
	
	public int getVisibleNameColor(Player player, int currentNameColor, boolean toMe)
	{
		if(isTeamed())
		{
			FightClubPlayer fPlayer = getFightClubPlayer(player);
			return fPlayer.getTeam().getNickColor();
		}
		return currentNameColor;
	}
	
	/*
	 * 
	 * Badges
	 * 
	 */
	protected int getBadgesEarned(FightClubPlayer fPlayer, int currentValue)
	{
		currentValue += addMultipleBadgeToPlayer(fPlayer, FightClubStatType.KILL_PLAYER, fPlayer.getKills(true), _badgesKillPlayer, fPlayer.getSecondsSpentOnEvent());
		currentValue += addMultipleBadgeToPlayer(fPlayer, FightClubStatType.KILL_PET, fPlayer.getKills(false), _badgesKillPet, fPlayer.getSecondsSpentOnEvent());
		currentValue += addMultipleBadgeToPlayer(fPlayer, FightClubStatType.DIE, fPlayer.getDeaths(), _badgesDie, fPlayer.getSecondsSpentOnEvent());
		
		currentValue += getRewardForRanking(fPlayer);
		
		currentValue += getRewardForWinningTeam(fPlayer);
		
		int minutesAFK = (int) Math.round((double)fPlayer.getTotalAfkSeconds()/60);
		FightClubLastStatsManager.setNewStat(fPlayer.getPlayer(), FightClubStatType.AFK_MINUTES, minutesAFK, BADGES_FOR_MINUTE_OF_AFK, fPlayer.getTotalAfkSeconds());
		
		for(FightClubAchievement ach : fPlayer.getCompletedAchievements())
		{
			currentValue += FightClubAchievements.getInstance().getReward(ach, this);
		}
		
		int minutesOnEvent = (int)Math.round((double)fPlayer.getSecondsSpentOnEvent()/60);
		currentValue += addMultipleBadgeToPlayer(fPlayer, FightClubStatType.TIME_ON_EVENT, minutesOnEvent, BADGES_PER_MINUTE_ON_EVENT, fPlayer.getSecondsSpentOnEvent());
		
		return currentValue;
	}
	
	/**
	 * @return score*badges
	 */
	protected int addMultipleBadgeToPlayer(FightClubPlayer fPlayer, FightClubStatType whatFor, int score, double badgePerScore, int secondsSpent)
	{
		int badgesEarned = (int) Math.floor(score*badgePerScore);
		FightClubLastStatsManager.setNewStat(fPlayer.getPlayer(), whatFor, score, badgesEarned, secondsSpent);
		return badgesEarned;
	}
	
	/**
	 * @return badges
	 */
	protected int addBadgeToPlayer(FightClubPlayer fPlayer, FightClubStatType whatFor, int score, int badgesEarned, int secondsSpent)
	{
		FightClubLastStatsManager.setNewStat(fPlayer.getPlayer(), whatFor, score, badgesEarned, secondsSpent);
		return badgesEarned;
	}

	protected int calculateBestPlaceReward(int place)//TODO fix it later, it has to also count FIGHTING_PLAYERS
	{
		switch(place)
		{
		case 0:
			return 10;
		case 1:
			return 5;
		case 2:
			return 2;
		}
		return 0;
	}
	
	private int getEndEventBadges(FightClubPlayer fPlayer)
	{
		return 0;
	}
	
	
	//===============================================================================================================
	//											Actions same for all events
	//===============================================================================================================
	
	public void startTeleportTimer(FightClubGameRoom room)
	{
		_room = room;
		room.setGame(this);
		
		setState(EVENT_STATE.COUNT_DOWN);
		
		for(Player player : room.getAllPlayers())
		{
			addObject(REGISTERED_PLAYERS, new FightClubPlayer(player));
			player.addEvent(this);
		}
		
		startNewTimer(true, 0, "teleportWholeRoomTimer", TIME_FIRST_TELEPORT);
	}
	
	public void addNewPlayer(Player player)
	{
		System.out.println("adding new player");
		addObject(REGISTERED_PLAYERS, new FightClubPlayer(player));
		player.addEvent(this);
	}
	
	public boolean canAddNewPlayer(Player player)
	{
		if(_state == EVENT_STATE.COUNT_DOWN)
			return true;
		if(_state == EVENT_STATE.PREPARATION && !_dontLetAnyoneIn)
			return true;
		return false;
	}
	
	/**
	 * teleportSinglePlayer for first time, healing and ressing - all REGISTERED_PLAYERS 
	 */
	protected void teleportRegisteredPlayers()
	{
		System.out.println("teleportRegisteredPlayers");
		for(FightClubPlayer player : getPlayers(REGISTERED_PLAYERS))
		{
			teleportSinglePlayer(player, true, true);
		}
	}
	
	/**
	 * Teleporting player to event random location, rooting if _state is PREPARATION or OVER, cancelling negative effects
	 * IF @firstTime - removing from REGISTERED_PLAYERS and adding to FIGHTING_PLAYERS, showing question mark, buffing, sending event menu, 
	 * sending messages about chat, setting invisibility if needed
	 */
	protected void teleportSinglePlayer(FightClubPlayer fPlayer, boolean firstTime, boolean healAndRess)
	{
		Player player = fPlayer.getPlayer();
		if(healAndRess)
		{
			if(player.isDead())
				player.doRevive(100.);
			player.setCurrentHpMp(player.getMaxHp(), player.getMaxMp(), true);
			player.setCurrentCp(player.getMaxCp());
		}
		
		Location[] spawns = null;
		Location loc = null;;
		
		if(!isTeamed())
			spawns = getMap().getPlayerSpawns();
		else
			loc = getTeamSpawn(fPlayer, true);
		
		if(!isTeamed())
			loc = getSafeLocation(spawns);
		
		//Make it a bit random
		loc = Location.findPointToStay(loc, 0, CLOSE_LOCATIONS_VALUE/2, fPlayer.getPlayer().getGeoIndex());
		
		if(isInstanced())
			player.teleToLocation(loc, getReflection());
		else
			player.teleToLocation(loc);
		
		if(_state == EVENT_STATE.PREPARATION || _state == EVENT_STATE.OVER)
			rootPlayer(player);
		
		//Cancelling negative effects
		cancelNegativeEffects(player);
		if(player.getPet() != null)
			cancelNegativeEffects(player.getPet());
		
		if(firstTime)
		{
			removeObject(REGISTERED_PLAYERS, fPlayer);
			addObject(FIGHTING_PLAYERS, fPlayer);
			
			player.store(true);
			player.sendPacket(new TutorialShowQuestionMark(100));
			BufferInstance.buffDefaultScheme(player);
			FightClubEventManager.getInstance().sendEventPlayerMenu(player);
			
			player.sendPacket(new Say2(player.getObjectId(), ChatType.ALL, getName(), "Normal Chat is visible for every player in event."));
			if(isTeamed())
			{
				player.sendPacket(new Say2(player.getObjectId(), ChatType.ALL, getName(), "Battlefield(^) Chat is visible only to your team!"));
				player.sendPacket(new Say2(player.getObjectId(), ChatType.BATTLEFIELD, getName(), "Battlefield(^) Chat is visible only to your team!"));
			}
		}
	}

	public void unregister(Player player)
	{
		FightClubPlayer fPlayer = getFightClubPlayer(player, REGISTERED_PLAYERS);
		player.removeEvent(this);
		removeObject(REGISTERED_PLAYERS, fPlayer);
		player.sendMessage("You are no longer registered!");
	}
	
	public boolean leaveEvent(Player player, boolean teleportTown)
	{
		if(player.isInCombat())
		{
			player.sendMessage("You cannot do that while being in Combat!");
			sendMessageToPlayer(player, MESSAGE_TYPES.CRITICAL, "Failed!");
			return false;
		}
		
		FightClubPlayer fPlayer = getFightClubPlayer(player);
		
		if(_state == EVENT_STATE.NOT_ACTIVE)
		{
			if(fPlayer.isInvisible())
				stopInvisibility(player);
			removeObject(FIGHTING_PLAYERS, fPlayer);
			if(isTeamed())
				fPlayer.getTeam().removePlayer(fPlayer);
			player.removeEvent(this);
			System.out.println("place:"+player);
			if(teleportTown)
				teleportBackToTown(player);
			else
				player.doRevive();
		}
		else //Leaving during event
		{
			System.out.println("leaveEvent");
			rewardPlayer(fPlayer);
			if(teleportTown)
				setInvisible(player, TIME_TELEPORT_BACK_TOWN, false);
			else
				setInvisible(player, -1, false);
			removeObject(FIGHTING_PLAYERS, fPlayer);
			//Killing player - protection for Last Man standing
			player.doDie(null);//Dont change order. Removing from 1. FIGHTING_PLAYERS 2. Do Die 3. Remove Event
			player.removeEvent(this);
			
			if(teleportTown)
				startNewTimer(false, 0, "teleportBackSinglePlayerTimer", TIME_TELEPORT_BACK_TOWN, player);
			else
				player.doRevive();
		}
		
		hideScores(player);
		
		if(getPlayers(FIGHTING_PLAYERS, REGISTERED_PLAYERS).isEmpty())//TODO if there are logged off players, take care of them
			destroyMe();
		
		return true;
	}
	
	public void loggedOut(Player player)
	{
		System.out.println("loggedOut, before registered:"+getPlayers(REGISTERED_PLAYERS).toArray()+" fighting:"+getPlayers(FIGHTING_PLAYERS).toArray());
		player.doDie(null);
		
		FightClubPlayer regFPlayer = getFightClubPlayer(player, REGISTERED_PLAYERS);
		if(regFPlayer != null)
		{
			removeObject(REGISTERED_PLAYERS, regFPlayer);
			
			if(isTeamed() && regFPlayer.getTeam() != null)
				regFPlayer.getTeam().removePlayer(regFPlayer);
		}
		
		FightClubPlayer figFPlayer = getFightClubPlayer(player, FIGHTING_PLAYERS);
		if(figFPlayer != null)
		{
			removeObject(FIGHTING_PLAYERS, figFPlayer);
			addObject(LOGGED_OFF_PLAYERS, figFPlayer);
			
			if(isTeamed())
				figFPlayer.getTeam().removePlayer(figFPlayer);
		}
	}
	/**
	 * If player logged off the event. Teleporting him back, setting current event to this, setting 10 sec invis, adding achievement thread
	 */
	public void loggedIn(Player player)
	{
		System.out.println("loggedIn");
		FightClubPlayer fPlayer = getFightClubPlayer(player, LOGGED_OFF_PLAYERS);
		
		if(fPlayer != null)
		{
			fPlayer.setPlayer(player);
			removeObject(LOGGED_OFF_PLAYERS, fPlayer);
			addObject(FIGHTING_PLAYERS, fPlayer);
			
			if(isTeamed())
			{
				if(fPlayer.getParty() != null && fPlayer.getParty().getMemberCount() < Party.MAX_SIZE)
					player.joinParty(fPlayer.getParty());
			}
			
			
			if(isHidePersonality())
				player.setPolyId(FightClubGameRoom.getPlayerClassGroup(player).getTransformId(), getReflection());
			
			if(!isRoundEvent())
				setInvisible(player, 10, true);
			
			player.addEvent(this);
			player.sendPacket(new TutorialShowQuestionMark(100));
			if(!isRoundEvent() || _state != EVENT_STATE.STARTED)
				teleportSinglePlayer(fPlayer, false, true);
			else if(getReflection() != null)
				player.teleToLocation(player.getLoc(), getReflection());
			
			updateScreenScores();
			
			//Achievement
			if(fPlayer.getAchProgress() == null)
				waitAndStartAchievement(fPlayer);
		}
	}
	
	protected void teleportBackToTown(Player player)
	{
		Location loc = Location.findPointToStay(FightClubEventManager.RETURN_LOC, 0, 100, player.getGeoIndex());
		player.teleToLocation(loc, ReflectionManager.DEFAULT);
		player.doRevive();
	}
	
	protected void rewardPlayer(FightClubPlayer fPlayer)
	{
		int badgesToGive = getBadgesEarned(fPlayer, 0);
		
		if(getState() == EVENT_STATE.NOT_ACTIVE)
			badgesToGive += getEndEventBadges(fPlayer);
		
		fPlayer.getPlayer().setLastEventBadgesEarned(badgesToGive);
		fPlayer.getPlayer().getInventory().addItem(FightClubEventManager.FIGHT_CLUB_BADGE_ID, badgesToGive);
		sendMessageToPlayer(fPlayer, MESSAGE_TYPES.SCREEN_BIG, "You have earned "+badgesToGive+" Fight Club Badge"+(badgesToGive != 1 ? "s" : "")+"!");
	}

	/**
	 * Make isTeamed() check before
	 * @wholeEvent - true - We won Last Man Standing! false - We Won Round!
	 * @winnerOfTheRound if wholeEvent == false, set it to null
	 */
	protected void announceWinnerTeam(boolean wholeEvent, FightClubTeam winnerOfTheRound)
	{
		int bestScore = -1;
		FightClubTeam bestTeam = null;
		boolean draw = false;
		if(wholeEvent)
		{
			for(FightClubTeam team : getTeams())
				if(team.getScore() > bestScore)
				{
					draw = false;
					bestScore = team.getScore();
					bestTeam = team;
				}
				else if(team.getScore() == bestScore)
				{
					draw = true;
				}
		}
		else
			bestTeam = winnerOfTheRound;
		
		if(!draw)
		{
			Say2 packet = new Say2(0, ChatType.COMMANDCHANNEL_ALL, bestTeam.getName()+" Team", "We won "+(wholeEvent ? getName() : " Round")+"!");
			for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
				iFPlayer.getPlayer().sendPacket(packet);
		}
		
		updateScreenScores();
	}

	/**
	 * It is checking all players by their scores. Not for teamed events!
	 * @wholeEvent - true - I won Last Man Standing! false - I Won Round!
	 * @winnerOfTheRound if wholeEvent == false, set it to null
	 */
	protected void announceWinnerPlayer(boolean wholeEvent, FightClubPlayer winnerOfTheRound)
	{
		int bestScore = -1;
		FightClubPlayer bestPlayer = null;
		boolean draw = false;
		if(wholeEvent)
		{
			for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
				if(iFPlayer.getPlayer() != null && iFPlayer.getPlayer().isOnline())
					if(iFPlayer.getScore() > bestScore)
					{
						bestScore = iFPlayer.getScore();
						bestPlayer = iFPlayer;
					}
					else if(iFPlayer.getScore() == bestScore)
					{
						draw = true;
					}
		}
		else
			bestPlayer = winnerOfTheRound;
		
		if(!draw)
		{
			Say2 packet = new Say2(0, ChatType.COMMANDCHANNEL_ALL, bestPlayer.getPlayer().getName(), "I Won "+(wholeEvent ? getName() : "Round")+"!");
			for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
				iFPlayer.getPlayer().sendPacket(packet);
		}
		
		updateScreenScores();
	}
	
	public void sendAchievementMessage(FightClubPlayer fPlayer, boolean sendFirstMsg, boolean sendScoreMsg)
	{
		if(fPlayer == null)
			return;
		
		if(fPlayer.isShowTutorial())
			FightClubEventManager.getInstance().sendEventPlayerMenu(fPlayer.getPlayer());
		
		if(fPlayer.getAchProgress() == null || _state == EVENT_STATE.OVER)
			return;
		
		Player player = fPlayer.getPlayer();
		String achievementName = FightClubAchievements.getInstance().getChangedName(fPlayer);
		
		if(sendScoreMsg)
			player.sendPacket(new Say2(player.getObjectId(), ChatType.COMMANDCHANNEL_ALL, "Achievement", achievementName));
		else if(sendFirstMsg)
			player.sendPacket(new Say2(player.getObjectId(), ChatType.COMMANDCHANNEL_ALL, "Achievement", achievementName+" \nReward: "+fPlayer.getAchievementReward()+" Badges!"));
		
		updateScreenScores();
	}
	
	protected void updateScreenScores()
	{
		String msg = getScreenScores(inScreenShowBeScoreNotKills(), inScreenShowBeTeamNotInvidual());
		
		for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
			sendMessageToPlayer(iFPlayer, MESSAGE_TYPES.SCREEN_SMALL, msg);
	}
	
	protected void updateScreenScores(Player player)
	{
		if(getFightClubPlayer(player) != null)
				sendMessageToPlayer(getFightClubPlayer(player), MESSAGE_TYPES.SCREEN_SMALL, getScreenScores(inScreenShowBeScoreNotKills(), inScreenShowBeTeamNotInvidual()));
	}
	
	protected void waitAndStartAchievements()
	{
		long endTimeInMillis = System.currentTimeMillis()+TIME_BEFORE_GIVING_ACHIEVEMENT*1000;
		for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
			_achievementWaitMap.put(iFPlayer, endTimeInMillis);

		//Checking thread
		if(!_achievementsThreadIsActive)
		{
			ThreadPoolManager.getInstance().schedule(new GiveAchievementThread(), 20000);
			_achievementsThreadIsActive = true;
		}
	}
	
	public void waitAndStartAchievement(final FightClubPlayer fPlayer)
	{
		long endTimeInMillis = System.currentTimeMillis()+TIME_BEFORE_GIVING_ACHIEVEMENT*1000;
		_achievementWaitMap.put(fPlayer, endTimeInMillis);
	}
	
	protected String getScorePlayerName(FightClubPlayer fPlayer)
	{
		return fPlayer.getPlayer().getName() + (isTeamed() ? " ("+fPlayer.getTeam().getName()+" Team)" : "");
	}
	
	/**
	 * Player Ranking is being updated
	 */
	protected void updatePlayerScore(FightClubPlayer fPlayer)
	{
		_scores.put(getScorePlayerName(fPlayer), fPlayer.getKills(true));
		_scoredUpdated = true;
			
		for(FightClubPlayer iterFPlayer : getPlayers(FIGHTING_PLAYERS))
			if(iterFPlayer.isShowRank())
				showScores(iterFPlayer.getPlayer());
		
		if(!isTeamed())
			updateScreenScores();
	}
	
	protected void showScores(Creature c)
	{
		Map<String, Integer> scores = getBestScores();
		
		FightClubPlayer fPlayer = getFightClubPlayer(c);
		fPlayer.setShowRank(true);
		
		c.sendPacket(new ExPVPMatchCCRecord(scores));
	}
	
	protected void hideScores(Creature c)
	{
		c.sendPacket(ExPVPMatchCCRetire.STATIC);
	}
	
	/**
	 * If player is AFK: getting him back from it
	 * If player isn't AFK: checking if he is in combat and asking if he really wants it
	 */
	private void handleAfk(FightClubPlayer fPlayer, boolean setAsAfk)
	{
		Player player = fPlayer.getPlayer();
		
		if(setAsAfk)
		{
			System.out.println("setting player as afk");
			fPlayer.setAfk(true);
			fPlayer.setAfkStartTime(player.getLastNotAfkTime());
			
			sendMessageToPlayer(player, MESSAGE_TYPES.CRITICAL, "You are considered as AFK Player!");
		}
		else
		{
			System.out.println("setting player as not afk");
			int totalAfkTime = (int)((System.currentTimeMillis() - fPlayer.getAfkStartTime()) / 1000);
			totalAfkTime -= TIME_TO_BE_AFK;
			if(totalAfkTime > 5)
			{
				fPlayer.setAfk(false);
				
				fPlayer.addTotalAfkSeconds(totalAfkTime);
				sendMessageToPlayer(player, MESSAGE_TYPES.CRITICAL, "You were afk for "+totalAfkTime+" seconds!");
			}
		}
	}
	
	/**
	 * @seconds - If seconds > 0, starts new timer that stopsInvisiblity when its over. 
	 * @SendMessages - Should timer send messages like VISIBLE IN X SECONDS?
	 */
	protected void setInvisible(Player player, int seconds, boolean sendMessages)
	{
		System.out.println("setInvisible");
		FightClubPlayer fPlayer = getFightClubPlayer(player);
		fPlayer.setInvisible(true);
		
		player.setInvisibleType(InvisibleType.NORMAL);
		player.startAbnormalEffect(AbnormalEffect.STEALTH);
		player.sendUserInfo(true);
		World.removeObjectFromPlayers(player);
		
		if(seconds > 0)
			startNewTimer(false, 0, "setInvisible", seconds, fPlayer, sendMessages);
	}
	
	protected void stopInvisibility(Player player)
	{
		System.out.println("stopInvisibility");
		FightClubPlayer fPlayer = getFightClubPlayer(player);
		
		if(fPlayer != null)
			fPlayer.setInvisible(false);
		
		player.setInvisibleType(InvisibleType.NONE);
		player.stopAbnormalEffect(AbnormalEffect.STEALTH);
		player.broadcastCharInfo();
		if(player.getPet() != null)
			player.getPet().broadcastCharInfo();
	}
	
	protected void rootPlayer(Player player)
	{
		if(!isRootBetweenRounds())
			return;
		List<Playable> toRoot = new ArrayList<>();
		toRoot.add(player);
		if(player.getPet() != null)
			toRoot.add(player.getPet());

		if(!player.isRooted())
		{
			player.startRooted();
		}
		player.stopMove();
		player.startAbnormalEffect(AbnormalEffect.ROOT);
	}

	protected void unrootPlayers()
	{
		if(!isRootBetweenRounds())
			return;
		System.out.println("unrootPlayers");
		for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
		{
			Player player = iFPlayer.getPlayer();
			if(player.isRooted())
			{
				player.stopRooted();
				player.stopAbnormalEffect(AbnormalEffect.ROOT);
			}
		}
	}
	
	protected void ressAndHealPlayers()
	{
		System.out.println("ressAndHealPlayers");
		for(FightClubPlayer fPlayer : getPlayers(FIGHTING_PLAYERS))
		{
			Player player = fPlayer.getPlayer();
			
			if(player.isDead())
				player.doRevive(100.);
			
			cancelNegativeEffects(player);
			if(player.getPet() != null)
				cancelNegativeEffects(player.getPet());
			
			player.setCurrentHpMp(player.getMaxHp(), player.getMaxMp());
			player.setCurrentCp(player.getMaxCp());
		}
	}
	
	protected int getWarRelation()
	{
		int result = 0;
		
		result |= RelationChanged.RELATION_CLAN_MEMBER;
		result |= RelationChanged.RELATION_1SIDED_WAR;
		result |= RelationChanged.RELATION_MUTUAL_WAR;
		
		return result;
	}
	
	protected int getFriendRelation()
	{
		int result = 0;
		
		result |= RelationChanged.RELATION_CLAN_MEMBER;
		result |= RelationChanged.RELATION_CLAN_MATE;
		
		return result;
	}
	
	/**
	 * Getting one location out of locs and spawning npc in there.
	 * Set @respawnInSeconds to 0 if you don't want them to respawn
	 */
	protected NpcInstance chooseLocAndSpawnNpc(int id, Location[] locs, int respawnInSeconds)
	{
		return spawnNpc(id, getSafeLocation(locs), respawnInSeconds);
	}
	
	/**
	 * Set @respawnInSeconds to 0 if you don't want them to respawn 
	 */
	protected NpcInstance spawnNpc(int id, Location loc, int respawnInSeconds)
	{
		SimpleSpawner spawn = new SimpleSpawner(id);
		spawn.setLoc(loc);
		spawn.setAmount(1);
		spawn.setHeading(loc.h);
		System.out.println("respawn Delay:"+Math.max(0, respawnInSeconds));
		spawn.setRespawnDelay(Math.max(0, respawnInSeconds));
		spawn.setReflection(getReflection());
		List<NpcInstance> npcs = spawn.initAndReturn();
		
		System.out.println("Respawn in secs:"+respawnInSeconds);
		if(respawnInSeconds <= 0)
			spawn.stopRespawn();
		
		return npcs.get(0);
	}
	
	/**
	 * @return "5 minutes" after pasting 300 seconds
	 */
	protected static String getFixedTime(int seconds)
	{
		int minutes = seconds/60;
		String result = "";
		if(seconds >= 60)
		{
			result = minutes + " minute" + (minutes > 1 ? "s" : ""); 
		}
		else
		{
			result = seconds + " second" + (seconds > 1 ? "s" : "");
		}
		return result;
	}

	public enum MESSAGE_TYPES {
		HERO,
		NORMAL_MESSAGE,
		SCREEN_BIG,
		SCREEN_SMALL,
		CRITICAL
	}
	
	protected void sendMessageToFightingAndRegistered(MESSAGE_TYPES type, String msg)
	{
		sendMessageToFighting(type, msg, false);
		sendMessageToRegistered(type, msg);
	}

	protected void sendMessageToTeam(FightClubTeam team, MESSAGE_TYPES type, String msg)
	{
		//Team Members
		for(FightClubPlayer iFPlayer : team.getPlayers())
			sendMessageToPlayer(iFPlayer, type, msg);
	}
	protected void sendMessageToFighting(MESSAGE_TYPES type, String msg, boolean skipJustTeleported)//TODO ogarnac skipJustTeleported, to juz nie jest potrzebne
	{
		//Fighting
		for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
			if(!skipJustTeleported || !iFPlayer.isInvisible())
				sendMessageToPlayer(iFPlayer, type, msg);
	}
	
	protected void sendMessageToRegistered(MESSAGE_TYPES type, String msg)
	{
		//Registered
		for(FightClubPlayer iFPlayer : getPlayers(REGISTERED_PLAYERS))
			sendMessageToPlayer(iFPlayer, type, msg);
	}
	
	public void sendMessageToPlayer(FightClubPlayer fPlayer, MESSAGE_TYPES type, String msg)
	{
		sendMessageToPlayer(fPlayer.getPlayer(), type, msg);
	}
	
	protected void sendMessageToPlayer(Player player, MESSAGE_TYPES type, String msg)
	{
		switch(type)
		{
		case HERO:
			player.sendPacket(new Say2(player.getObjectId(), ChatType.HERO_VOICE, getName(), msg));
			break;
		case NORMAL_MESSAGE:
			player.sendMessage(msg);
			break;
		case SCREEN_BIG:
			player.sendPacket(new ExShowScreenMessage(msg, 3000, ScreenMessageAlign.TOP_CENTER, true));
			updateScreenScores(player);
			break;
		case SCREEN_SMALL:
			player.sendPacket(new ExShowScreenMessage(msg, 600000, ScreenMessageAlign.TOP_LEFT, false));
			break;
		case CRITICAL:
			player.sendPacket(new Say2(player.getObjectId(), ChatType.COMMANDCHANNEL_ALL, player.getName(), msg));
			updateScreenScores(player);
			break;
		}
	}

	public void setState(EVENT_STATE state)
	{
		_state = state;
	}
	
	//===============================================================================================================
	//												Event Getters
	//===============================================================================================================

	
	public EVENT_STATE getState()
	{
		return _state;
	}
	
	public int getObjectId()
	{
		return _objId;
	}
	
	public int getEventId()
	{
		return getId();
	}
	
	public String getDescription()
	{
		return _desc;
	}
	
	public FightClubMap getMap()
	{
		return _map;
	}
	
	public boolean isTeamed()
	{
		return _teamed;
	}
	
	protected boolean isInstanced()
	{
		return _instanced;
	}
	
	@Override
	public Reflection getReflection()
	{
		return _reflection;
	}
	
	/**
	 * @return -1 in case 
	 */
	public int getRoundRuntime()
	{
		return _roundRunTime;
	}
	
	public int getRespawnTime()
	{
		return _respawnTime;
	}
	
	public boolean isRoundEvent()
	{
		return _roundEvent;
	}
	
	public int getTotalRounds()
	{
		return _rounds;
	}
	
	public int getCurrentRound()
	{
		return _currentRound;
	}
	
	public boolean getBuffer()
	{
		return _buffer;
	}

	protected boolean isRootBetweenRounds()
	{
		return _rootBetweenRounds;
	}
	
	public boolean isLastRound()
	{
		return !isRoundEvent() || getCurrentRound() == getTotalRounds();
	}
	
	protected List<FightClubTeam> getTeams()
	{
		return _teams;
	}
	
	public MultiValueSet<String> getSet()
	{
		return _set;
	}
	
	public void clearSet()
	{
		_set = null;
	}
	
	public CLASSES[] getExcludedClasses()
	{
		return _excludedClasses;
	}
	
	public boolean isHidePersonality()
	{
		return !_showPersonality;
	}
	
	protected int getTeamTotalKills(FightClubTeam team)
	{
		if(!isTeamed())
			return 0;
		int totalKills = 0;
		for(FightClubPlayer iFPlayer : team.getPlayers())
			totalKills += iFPlayer.getKills(true);
		
		return totalKills;
	}
	
	/**
	 * @return players count from the groups
	 */
	public int getPlayersCount(String... groups)
	{
		return getPlayers(groups).size();
	}
	
	public List<FightClubPlayer> getPlayers(String... groups)
	{
		if(groups.length == 1)
		{
			List<FightClubPlayer> fPlayers = getObjects(groups[0]);
			return fPlayers;
		}
		else
		{
			List<FightClubPlayer> newList = new ArrayList<>();
			for(String group : groups)
			{
				List<FightClubPlayer> fPlayers = getObjects(group);
				newList.addAll(fPlayers);
			}
			return newList;
		}
	}
	
	public List<Player> getAllFightingPlayers()
	{
		List<FightClubPlayer> fPlayers = getPlayers(FIGHTING_PLAYERS);
		List<Player> players = new ArrayList<>(fPlayers.size());
		for(FightClubPlayer fPlayer : fPlayers)
			players.add(fPlayer.getPlayer());
		return players;
	}
	
	public List<Player> getMyTeamFightingPlayers(Player player)
	{
		FightClubTeam fTeam = getFightClubPlayer(player).getTeam();
		List<FightClubPlayer> fPlayers = getPlayers(FIGHTING_PLAYERS);
		List<Player> players = new ArrayList<>(fPlayers.size());
		
		if(!isTeamed())
		{
			player.sendPacket(new Say2(player.getObjectId(), ChatType.BATTLEFIELD, getName(), "(There are no teams, only you can see the message)"));
			players.add(player);
		}
		else
		{
			for(FightClubPlayer iFPlayer : fPlayers)
				if(iFPlayer.getTeam().equals(fTeam))
					players.add(iFPlayer.getPlayer());
		}
		return players;
	}
	
	/**
	 * Looking for FightClubPlayer ONLY in FIGHTING_PLAYERS
	 */
	public FightClubPlayer getFightClubPlayer(Creature creature)
	{
		return getFightClubPlayer(creature, FIGHTING_PLAYERS);
	}
	
	/**
	 * Looking for FightClubPlayer in specific groups
	 */
	public FightClubPlayer getFightClubPlayer(Creature creature, String... groups)
	{
		if(!creature.isPlayable())
			return null;
		
		int lookedPlayerId = creature.getPlayer().getObjectId();
		
		for(FightClubPlayer iFPlayer : getPlayers(groups))
			if(iFPlayer.getPlayer().getObjectId() == lookedPlayerId)
				return iFPlayer;
		return null;
	}
	
	
	//===============================================================================================================
	//												Private actions
	//===============================================================================================================

	private void chooseMap()
	{
		//Getting event name
		String eventName = Util.getChangedEventName(this);
		//Choosing map randomly
		System.out.println("maps found:"+FightClubMapHolder.getInstance().getMapsForEvent(eventName).size());
		_map = Rnd.get(FightClubMapHolder.getInstance().getMapsForEvent(eventName));//TODO players and team count
	}
	
	/**
	 * Getting teams and partys from FightClubGameRoom and making them actually exist
	 */
	private void spreadIntoTeamsAndPartys()
	{
		//Creating teams
		for(int i = 0 ; i < _room.getTeamsCount() ; i++)
			_teams.add(new FightClubTeam(i+1));
		
		int index = 0;
		for(FightClubRoomTeam team : _room.getTeams())
		{
			FightClubTeam fTeam = _teams.get(index);
			for(FightClubRoomParty party : team.getPartys())
			{
				Party newParty = null;
				List<FightClubPlayer> playersToParty = new ArrayList<>();
				
				for(Player player : party.getMembers())
				{
					//Adding to team
					FightClubPlayer fPlayer = getFightClubPlayer(player, REGISTERED_PLAYERS);
					if(fPlayer == null)
						continue;
					fPlayer.setTeam(fTeam);
					fTeam.addPlayer(fPlayer);
					
					//Removing player from current party
					if(player.getParty() != null)
						player.getParty().removePartyMember(player, true);
					
					playersToParty.add(fPlayer);
				}
				
				//Adding player to new party
				if(playersToParty.size() > 1)
				{
					for(FightClubPlayer iFPlayer : playersToParty)
					{
						Player player = iFPlayer.getPlayer();
						if(newParty == null)
						{
							player.setParty(newParty = new Party(player, Party.ITEM_ORDER_SPOIL));
						}
						else
						{
							player.joinParty(newParty);
						}
						iFPlayer.setParty(newParty);
					}
				}
			}
			index++;
		}
	}
	
	private synchronized void createReflection(IntObjectMap<DoorTemplate> doors, Map<String, ZoneTemplate> zones)
	{
		InstantZone iz = InstantZoneHolder.getInstance().getInstantZone(INSTANT_ZONE_ID);
		
		_reflection = new Reflection();
		_reflection.init(iz);
		_reflection.init(doors, zones);
		
		for(Zone zone : _reflection.getZones())
			zone.addListener(_zoneListener);
	}
	/**
	 * @return Finding location where nobodyIsClose
	 */
	private Location getSafeLocation(Location[] locations)
	{
		System.out.println("before second");
		Location safeLoc = null;
		int checkedCount = 0;
		boolean isOk = false;
		
		while(!isOk)
		{
			safeLoc = Rnd.get(locations);
			
			//Checking if nobody is close to spawn, only in single player events
			isOk = nobodyIsClose(safeLoc);
			checkedCount++;
				
			//If players are close to every spawn, choose something anyway.
			if(checkedCount > locations.length*2)
				isOk = true;
		}
		System.out.println("Safe loc:"+safeLoc);
		return safeLoc;
	}
	
	/**
	 * @randomNotClosestToPt If true - @return random location from team locs
	 * @randomNotClosestToPt If false - @return location closest to team members
	 */
	protected Location getTeamSpawn(FightClubPlayer fPlayer, boolean randomNotClosestToPt)
	{
		FightClubTeam team = fPlayer.getTeam();
		Location[] spawnLocs = getMap().getTeamSpawns().get(team.getIndex());
		
		if(randomNotClosestToPt || _state != EVENT_STATE.STARTED)
		{
			if(team.getSpawnLoc() == null)
			{
				team.setSpawnLoc(Rnd.get(spawnLocs));
			}
			return team.getSpawnLoc();
		}
		else
		{
			List<Player> playersToCheck = new ArrayList<>();
			if(fPlayer.getParty() != null)
				playersToCheck = fPlayer.getParty().getPartyMembers();
			else
				for(FightClubPlayer iFPlayer : team.getPlayers())
					playersToCheck.add(iFPlayer.getPlayer());
			
			Map<Location, Integer> spawnLocations = new FastMap<>(spawnLocs.length);
			for(Location loc : spawnLocs)
				spawnLocations.put(loc, 0);
			
			for(Player player : playersToCheck)
				if(player != null && player.isOnline() && !player.isDead())
				{
					Location winner = null;
					double winnerDist = -1;
					for(Location loc : spawnLocs)
						if(winnerDist <= 0 || winnerDist < player.getDistance(loc))
						{
							winner = loc;
							winnerDist = player.getDistance(loc);
						}
					
					if(winner != null)
						spawnLocations.put(winner, spawnLocations.get(winner) + 1);
				}
			
			Location winner = null;
			double points = -1;
			for(Entry<Location, Integer> spawn : spawnLocations.entrySet())
				if(points < spawn.getValue())
				{
					winner = spawn.getKey();
					points = spawn.getValue();
				}
			
			if(points <= 0)
				return Rnd.get(spawnLocs);
			return winner;
		}
	}
	
	/**
	 * Choosing achievement, choosing target, choosing action, saving achievement to fPlayer, 
	 * updating message and telling FightClubAchievements about it
	 */
	private void startAchievement(FightClubPlayer fPlayer)
	{
		//Choosing achievement
		FightClubAchievement ach = FightClubAchievements.getInstance().getAchievementForEvent(this, fPlayer);
		if(ach == null)
			return;
		//Choosing target
		Object target = setupAchivementTarget(ach, fPlayer);
		//Choosing action
		Object action = getActionForAchievement(ach, fPlayer);
		//Saving achievement to fPlayer
		fPlayer.setCurrentAchievement(ach, target, action);
		//Updating message
		sendAchievementMessage(fPlayer, true, false);
		//Telling FightClubAchievements about it
		FightClubAchievements.getInstance().startedNewAchievement(fPlayer);
	}
	
	/**
	 * Sending rewardPlayer method to every FIGHTING_PLAYERS
	 */
	private void giveRewards()
	{
		for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
			rewardPlayer(iFPlayer);
	}
	
	/**
	 * @return 25 <Name, Points> - players with most points
	 */
	private Map<String, Integer> getBestScores()
	{
		if(_scores.size() <= 25)
			return _scores;
		
		if(!_scoredUpdated)
			return _bestScores;
		
		List<Integer> points = new ArrayList<Integer>(_scores.values());
		Collections.sort(points);
		Collections.reverse(points);
		
		int cap = points.get(Math.min(25, points.size()-1));
		Map<String, Integer> finalResult = new FastMap<>();
		
		for(Entry<String, Integer> i : _scores.entrySet())
			if(i.getValue() > cap)
				finalResult.put(i.getKey(), i.getValue());
		
		if(finalResult.size() < 25)
		{
			for(Entry<String, Integer> i : _scores.entrySet())
				if(i.getValue() == cap)
				{
					finalResult.put(i.getKey(), i.getValue());
					if(finalResult.size() == 25)
						break;
				}
		}
		
		_bestScores = finalResult;
		_scoredUpdated = false;
		
		return finalResult;
	}
	
	/**
	 * Updating score of every FIGHTING_PLAYERS
	 */
	private void updateEveryScore()
	{
		for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
		{
			_scores.put(getScorePlayerName(iFPlayer), iFPlayer.getKills(true));
			_scoredUpdated = true;
		}
	}
	
	/**
	 * @showScoreNotKills - true - Score: - false - "Kills:"
	 * @teamPointsNotInvidual - true: Team Score/Kills, false: Player Score/Kills
	 */
	private String getScreenScores(boolean showScoreNotKills, boolean teamPointsNotInvidual)
	{
		String msg = "";
		if(isTeamed() && teamPointsNotInvidual)
		{
			List<FightClubTeam> teams = getTeams();
			Collections.sort(teams, new BestTeamComparator(showScoreNotKills));
			for(FightClubTeam team : teams)
				msg += team.getName()+" Team: "+(showScoreNotKills ? team.getScore() : getTeamTotalKills(team))+" "+(showScoreNotKills ? "Points" : "Kills")+"\n";
		}
		else
		{
			List<FightClubPlayer> fPlayers = getPlayers(FIGHTING_PLAYERS);
			List<FightClubPlayer> changedFPlayers = new ArrayList<>(fPlayers.size());
			changedFPlayers.addAll(fPlayers);
			
			Collections.sort(changedFPlayers, new BestPlayerComparator(showScoreNotKills));
			int max = Math.min(10, changedFPlayers.size());
			for(int i = 0;i<max;i++)
				msg += changedFPlayers.get(i).getPlayer().getName()+" "+(showScoreNotKills ? "Score" : "Kills")+": "+(showScoreNotKills ? changedFPlayers.get(i).getScore() : changedFPlayers.get(i).getKills(true))+"\n";
		}
		
		return msg;
	}
	
	protected int getRewardForWinningTeam(FightClubPlayer fPlayer)
	{
		if(!isTeamed())
			return 0;
		
		FightClubTeam winner = null;
		int winnerPoints = -1;
		boolean sameAmount = false;
		for(FightClubTeam team : getTeams())
		{
			if(team.getScore() > winnerPoints)
			{
				winner = team;
				winnerPoints = team.getScore();
				sameAmount = false;
			}
			else if(team.getScore() == winnerPoints)
			{
				sameAmount = true;
			}
		}
		
		if(!sameAmount && fPlayer.getTeam().equals(winner))
		{
			return addBadgeToPlayer(fPlayer, FightClubStatType.WON_EVENT, 1, BADGES_FOR_MEMBER_OF_WINNING_TEAM, fPlayer.getSecondsSpentOnEvent());
		}
		else
		{
			return 0;
		}
	}
	
	/**
	 * If player was best of 3, his score was added to StatsManager and badges reward is @returned
	 * @return badges reward
	 */
	private int getRewardForRanking(FightClubPlayer fPlayer)
	{
		List<Integer> points = new ArrayList<Integer>(_scores.values());
		Collections.sort(points);
		Collections.reverse(points);
		int place = 500;
		int index = 0;
		for(Integer bestPoint : points)
		{
			System.out.println("best point:"+bestPoint+" index:"+index);
			if(fPlayer.getKills(true) == bestPoint)
			{
				place = index;
				break;
			}
			index++;
			if(index >= 3)
				return 0;
		}
		if(place == 500)
			return 0;
		
		int reward = calculateBestPlaceReward(place);
		if(reward != 0)
		{
			FightClubStatType type;
			if(place == 0)
				type = FightClubStatType.FIRST_PLACE;
			else if(place == 1)
				type = FightClubStatType.SECOND_PLACE;
			else
				type = FightClubStatType.THIRD_PLACE;
			
			return addBadgeToPlayer(fPlayer, type, place+1, reward, fPlayer.getSecondsSpentOnEvent());
		}
		
		return 0;
	}
	
	/**
	 * @return checking if nobody is near(CLOSE_LOCATIONS_VALUE) loc
	 */
	private boolean nobodyIsClose(Location loc)
	{
		for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
		{
			Location playerLoc = iFPlayer.getPlayer().getLoc();
			if(Math.abs(playerLoc.getX()-loc.getX()) <= CLOSE_LOCATIONS_VALUE)
				return false;
			if(Math.abs(playerLoc.getY()-loc.getY()) <= CLOSE_LOCATIONS_VALUE)
				return false;
		}
		return true;
	}
	
	/**
	 * Checking every REGISTERED_PLAYERS if he meets criteria
	 */
	private void checkIfRegisteredMeetCriteria()
	{
		System.out.println("checking");
		for(FightClubPlayer iFPlayer : getPlayers(REGISTERED_PLAYERS))
		{
			System.out.println("checking:"+iFPlayer.getPlayer().getName());
			checkIfRegisteredPlayerMeetCriteria(iFPlayer);
		}
	}
	
	/**
	 * If he doesn't, unregistering player
	 * @return player meets criteria
	 */
	private boolean checkIfRegisteredPlayerMeetCriteria(FightClubPlayer fPlayer)
	{
		System.out.println("player:"+fPlayer.getPlayer());
		if(!FightClubEventManager.getInstance().canPlayerParticipate(fPlayer.getPlayer(), true, _room, false))
		{
			System.out.println("returning false");
			return false;
		}
		else
		{
			System.out.println("returning true");
			return true;
		}
	}
	
	/**
	 * Removing all debuffs
	 */
	private void cancelNegativeEffects(Playable playable)
	{
		List<Effect> _buffList = new ArrayList<Effect>();
		
		for(Effect e : playable.getEffectList().getAllEffects())
		{
			if(e.isOffensive() && e.isCancelable())
				_buffList.add(e);
		}
		
		for(Effect e : _buffList)
		{
			e.exit();
		}
	}
	
	/**
	 * @param classes like TANKS;DAMAGE_DEALERS
	 * @return array of CLASS_TYPES
	 */
	private CLASSES[] parseExcludedClasses(String classes)
	{
		if(classes.equals(""))
			return new CLASSES[0];
		
		String[] classType = classes.split(";");
		CLASSES[] realTypes = new CLASSES[classType.length];
		
		for(int i = 0;i<classType.length;i++)
			realTypes[i] = CLASSES.valueOf(classType[i]);
		
		return realTypes;
	}
	
	private int[] parseExcludedSkills(String ids)
	{
		if(ids == null || ids.isEmpty())
			return null;
		StringTokenizer st = new StringTokenizer(ids, ";");
		int[] realIds = new int[st.countTokens()];
		int index = 0;
		while(st.hasMoreTokens())
		{
			realIds[index] = Integer.parseInt(st.nextToken());
			index++;
		}
		return realIds;
	}
	
	/**
	 * Stops: {5, 15, 30, 60, 300, 600, 900}
	 */
	private int getTimeToWait(int totalLeftTimeInSeconds)
	{
		int toWait = 1;
		
		int[] stops = {5, 15, 30, 60, 300, 600, 900};
		
		for(int stop : stops)
			if(totalLeftTimeInSeconds > stop)
				toWait = stop;
		
		return toWait;
	}
	
	/**
	 * Thread that adds Seconds spent on event to every fPlayer
	 */
	private class TimeSpentOnEventThread extends RunnableImpl
	{
		@Override
		public void runImpl() throws Exception
		{
			if(_state == EVENT_STATE.STARTED)
			{
				for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
				{
					if(iFPlayer.getPlayer() == null || !iFPlayer.getPlayer().isOnline())
						continue;
					
					if(iFPlayer.isAfk())
						continue;
					
					iFPlayer.incSecondsSpentOnEvent(10);
				}
			}
			
			if(_state != EVENT_STATE.NOT_ACTIVE)
				ThreadPoolManager.getInstance().schedule(new TimeSpentOnEventThread(), 10*1000);
		}
	}
	
	private class GiveAchievementThread extends RunnableImpl
	{
		@Override
		public void runImpl() throws Exception
		{
			List<FightClubPlayer> toDelete = new ArrayList<>();
			long currentTime = System.currentTimeMillis();
			
			for(Entry<FightClubPlayer, Long> entry : _achievementWaitMap.entrySet())
			{
				if(entry.getValue() <= currentTime)
				{
					FightClubPlayer fPlayer = entry.getKey();
					if(_state == EVENT_STATE.STARTED && fPlayer.getPlayer().isOnline() && getFightClubPlayer(fPlayer.getPlayer()) != null && !fPlayer.isAfk())
					{
						startAchievement(entry.getKey());
					}
					toDelete.add(entry.getKey());
				}
			}
			
			for(FightClubPlayer iFPlayerToDelete : toDelete)
				_achievementWaitMap.remove(iFPlayerToDelete);
			
			if(_state != EVENT_STATE.NOT_ACTIVE)
				ThreadPoolManager.getInstance().schedule(new GiveAchievementThread(), 5000);
		}
		
	}

	private class LeftZoneThread extends RunnableImpl
	{
		@Override
		public void runImpl() throws Exception
		{
			List<FightClubPlayer> toDelete = new ArrayList<>();
			Say2 packet = new Say2(0, ChatType.COMMANDCHANNEL_ALL, "Error", "Go Back To Event Zone!");
			
			for(Entry<FightClubPlayer, Zone> entry : _leftZone.entrySet())
			{
				Player player = entry.getKey().getPlayer();
				if(player == null || !player.isOnline() || _state == EVENT_STATE.NOT_ACTIVE || entry.getValue().checkIfInZone(player) || player.isDead()
						|| player.isTeleporting())
				{
					toDelete.add(entry.getKey());
					continue;
				}
				
				int power = (int)Math.max(400, entry.getValue().findDistanceToZone(player, true)-4000);
				System.out.println("power:"+power);
				player.sendPacket(new Earthquake(player.getLoc(), power, 5));
				player.sendPacket(packet);
				entry.getKey().increaseSecondsOutsideZone();
				
				if(entry.getKey().getSecondsOutsideZone() >= TIME_MAX_SECONDS_OUTSIDE_ZONE)
				{
					player.doDie(null);
					toDelete.add(entry.getKey());
					entry.getKey().clearSecondsOutsideZone();
				}
			}
			
			for(FightClubPlayer playerToDelete : toDelete)
			{
				if(playerToDelete != null)
				{
					_leftZone.remove(playerToDelete);
					playerToDelete.clearSecondsOutsideZone();
				}
			}
			
			if(_state != EVENT_STATE.NOT_ACTIVE)
				ThreadPoolManager.getInstance().schedule(this, 1000L);
		}
	}
	
	private class CheckAfkThread extends RunnableImpl
	{
		@Override
		public void runImpl() throws Exception
		{
			long currentTime = System.currentTimeMillis();
			for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
			{
				Player player = iFPlayer.getPlayer();
				boolean isAfk = (player.getLastNotAfkTime() + TIME_TO_BE_AFK*1000) < currentTime;
				
				if(iFPlayer.isAfk())
				{
					if(!isAfk)
					{
						handleAfk(iFPlayer, false);//Just came back from afk
					}
					else if(_state != EVENT_STATE.OVER)
					{
						sendMessageToPlayer(player, MESSAGE_TYPES.CRITICAL, "You are in AFK mode!");
					}
				}
				else if(_state == EVENT_STATE.NOT_ACTIVE)
				{
					handleAfk(iFPlayer, false);
				}
				else if(isAfk)
				{
					handleAfk(iFPlayer, true);//Just started to be afk
				}
			}
			
			if(getState() != EVENT_STATE.NOT_ACTIVE)
			{
				ThreadPoolManager.getInstance().schedule(this, 1000);
			}
			else
			{
				for(FightClubPlayer iFPlayer : getPlayers(FIGHTING_PLAYERS))
				{
					if(iFPlayer.isAfk())
						handleAfk(iFPlayer, false);
				}
			}
		}
	}
	
	private class BestTeamComparator implements Comparator<FightClubTeam>
	{
		private boolean _scoreNotKills;
		private BestTeamComparator(boolean scoreNotKills)
		{
			_scoreNotKills = scoreNotKills;
		}
		
		@Override
		public int compare(FightClubTeam arg0, FightClubTeam arg1)
		{
			if(_scoreNotKills)
				return Integer.compare(arg1.getScore(), arg0.getScore());
			else
			{
				return Integer.compare(getTeamTotalKills(arg1), getTeamTotalKills(arg0));
			}
		}
	}
	
	private class BestPlayerComparator implements Comparator<FightClubPlayer>
	{
		private boolean _scoreNotKills;
		private BestPlayerComparator(boolean scoreNotKills)
		{
			_scoreNotKills = scoreNotKills;
		}
		
		@Override
		public int compare(FightClubPlayer arg0, FightClubPlayer arg1)
		{
			if(_scoreNotKills)
				return Integer.compare(arg1.getScore(), arg0.getScore());
			else
				return Integer.compare(arg1.getKills(true), arg0.getKills(true));
		}
	}
	
	//===============================================================================================================
	//												Event Timers
	//===============================================================================================================
	
	/**
	 * Big Timer - Waiting for first players, later registered teleporting to event
	 */
	@Deprecated
	public static boolean teleportWholeRoomTimer(int eventObjId, int secondsLeft)
	{
		AbstractFightClub event = FightClubEventManager.getInstance().getEventByObjId(eventObjId);
		if(secondsLeft == 0)
		{
			event._dontLetAnyoneIn = true;
			event.startEvent();
		}
		else
		{
			event.checkIfRegisteredMeetCriteria();
			event.sendMessageToRegistered(MESSAGE_TYPES.HERO, "You are going to be teleported in "+getFixedTime(secondsLeft)+"!");
		}
		return true;
	}
	
	/**
	 * Big Timer - Starting round timer
	 */
	@Deprecated
	public static boolean startRoundTimer(int eventObjId, int secondsLeft)
	{
		AbstractFightClub event = FightClubEventManager.getInstance().getEventByObjId(eventObjId);
		
		if(secondsLeft > 0)
		{
			String firstWord;
			if(event.isRoundEvent())
				firstWord = ((event.getCurrentRound() + 1) == event.getTotalRounds() ? "Last" : ROUND_NUMBER_IN_STRING[event.getCurrentRound()+1]) + " Round";
			else
				firstWord = "Match";
			String message = firstWord+" is going to start in "+getFixedTime(secondsLeft)+"!";
			event.sendMessageToFighting(MESSAGE_TYPES.SCREEN_BIG, message, true);
		}
		else
			event.startRound();
		
		return true;
	}
	
	/**
	 * Big timer - Watching when event ends
	 */
	@Deprecated
	public static boolean endRoundTimer(int eventObjId, int secondsLeft)
	{
		AbstractFightClub event = FightClubEventManager.getInstance().getEventByObjId(eventObjId);
		if(secondsLeft > 0)
			event.sendMessageToFighting(MESSAGE_TYPES.SCREEN_BIG, (!event.isLastRound() ? "Round" : "Match")+" is going to be Over in "+getFixedTime(secondsLeft)+"!", false);
		else
			event.endRound();
		
		return true;
	}
	
	/**
	 * Small timer - shutdown
	 */
	@Deprecated
	public static boolean shutDownTimer(int eventObjId, int secondsLeft)
	{
		AbstractFightClub event = FightClubEventManager.getInstance().getEventByObjId(eventObjId);
		
		if(!FightClubEventManager.getInstance().serverShuttingDown())
		{
			event._dontLetAnyoneIn = false;
			return false;
		}
		
		if(secondsLeft < 180)
		{
			//Check to make it just once
			if(!event._dontLetAnyoneIn)
			{
				event.sendMessageToRegistered(MESSAGE_TYPES.CRITICAL, "You are no longer registered because of Shutdown!");
				for(FightClubPlayer player : event.getPlayers(REGISTERED_PLAYERS))
				{
					event.unregister(player.getPlayer());
				}
				event.getObjects(REGISTERED_PLAYERS).clear();
				event._dontLetAnyoneIn = true;
			}
		}
		
		if(secondsLeft < 60)
		{
			event._timer.cancel(false);
			event.sendMessageToFighting(MESSAGE_TYPES.CRITICAL, "Event ended because of Shutdown!", false);
			event.setState(EVENT_STATE.OVER);
			event.stopEvent();
			
			event._dontLetAnyoneIn = false;
			return false;
		}
		return true;
	}
	
	/**
	 * Small Timer - Teleporting player back from the event. Remove him from players list before
	 */
	@Deprecated
	public static boolean teleportBackSinglePlayerTimer(int eventObjId, int secondsLeft, Player player)
	{
		AbstractFightClub event = FightClubEventManager.getInstance().getEventByObjId(eventObjId);
		
		if(player == null || !player.isOnline())
			return false;
		
		if(secondsLeft > 0)
			event.sendMessageToPlayer(player, MESSAGE_TYPES.SCREEN_BIG, "You are going to be teleported back in "+getFixedTime(secondsLeft)+"!");
		else
		{
			event.teleportBackToTown(player);
		}
		
		return true;
	}
	
	/**
	 * Small Timer - Ressurecting player
	 */
	@Deprecated
	public static boolean ressurectionTimer(int eventObjId, int secondsLeft, FightClubPlayer fPlayer)
	{
		AbstractFightClub event = FightClubEventManager.getInstance().getEventByObjId(eventObjId);
		Player player = fPlayer.getPlayer();

		if(player == null || !player.isOnline() || !player.isDead())
			return false;
		
		if(secondsLeft > 0)
			player.sendMessage("Respawn in "+getFixedTime(secondsLeft)+"!");
		else
		{
			event.hideScores(player);
			event.teleportSinglePlayer(fPlayer, false, true);
		}
		return true;
	}
	
	/**
	 * Small Timer - Making player visible again
	 */
	@Deprecated
	public static boolean setInvisible(int eventObjId, int secondsLeft, FightClubPlayer fPlayer, boolean sendMessages)
	{
		AbstractFightClub event = FightClubEventManager.getInstance().getEventByObjId(eventObjId);
		if(fPlayer.getPlayer() == null || !fPlayer.getPlayer().isOnline())
			return false;
		
		System.out.println("inside setInvisible, seconds:"+secondsLeft);
		if(secondsLeft > 0)
		{
			if(sendMessages)
				event.sendMessageToPlayer(fPlayer, MESSAGE_TYPES.SCREEN_BIG, "Visible in "+getFixedTime(secondsLeft)+"!");
		}
		else
		{
			System.out.println("Event started:"+(event.getState() == EVENT_STATE.STARTED));
			if(sendMessages && event.getState() == EVENT_STATE.STARTED)
				event.sendMessageToPlayer(fPlayer, MESSAGE_TYPES.SCREEN_BIG, "Fight!");
			event.stopInvisibility(fPlayer.getPlayer());
		}
		return true;
		
	}
	
	/**
	 * Starting new Small or Big timer
	 * @saveAsMainTimer - should it be save to _timer?
	 * @firstWaitingTimeInMilis - time before sending first msg
	 * @methodName - name of the method that will be run, between every sleep and at the end
	 * @args - arguments that method takes(except int eventObjId)
	 */
	public void startNewTimer(boolean saveAsMainTimer, int firstWaitingTimeInMilis, String methodName, Object... args)
	{
		ScheduledFuture<?> timer = ThreadPoolManager.getInstance().schedule(new SmartTimer(methodName, saveAsMainTimer, args), firstWaitingTimeInMilis);
	
		if(saveAsMainTimer)
			_timer = timer;
	}
	
	private class SmartTimer extends RunnableImpl
	{
		private final String _methodName;
		private final Object[] _args;
		private final boolean _saveAsMain;
		
		private SmartTimer(String methodName, boolean saveAsMainTimer, Object... args)
		{
			_methodName = methodName;
			
			Object[] changedArgs = new Object[args.length+1];
			changedArgs[0] = getObjectId();
			for(int i = 0;i<args.length;i++)
				changedArgs[i+1] = args[i];
			_args = changedArgs;
			_saveAsMain = saveAsMainTimer;
		}
		
		@Override
		public void runImpl() throws Exception
		{
			//Preparing parameters
			Class<?>[] parameterTypes = new Class<?>[_args.length];
			for(int i = 0; i < _args.length; i++)
				parameterTypes[i] = _args[i] != null ? _args[i].getClass() : null;
			
			int waitingTime = (int) _args[1];
				
			try
			{
				System.out.println("Running "+_methodName+" seconds:"+waitingTime);
				Object ret = MethodUtils.invokeMethod(AbstractFightClub.this, _methodName, _args, parameterTypes);
					
				if((boolean)ret == false)
					return;
			}
			catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e)
			{
				e.printStackTrace();
			}
				
			if(waitingTime > 0)
			{
				int toWait = getTimeToWait(waitingTime);
	
				waitingTime -= toWait;
					
				_args[1] = waitingTime;
					
				ScheduledFuture<?> timer = ThreadPoolManager.getInstance().schedule(this, toWait*1000);
				
				if(_saveAsMain)
					_timer = timer;
			}
			else
				return;
		}
	}

	@Override
	public void reCalcNextTime(boolean onInit)
	{
		clearActions();

		registerActions();
	}

	@Override
	protected long startTimeMillis()
	{
		return 0;
	}

	@Override
	public void onAddEvent(GameObject o)
	{
		if(o.isPlayer())
		{
			o.getPlayer().addListener(_exitListener);
		}
	}

	@Override
	public void onRemoveEvent(GameObject o)
	{
		if(o.isPlayer())
		{
			o.getPlayer().removeListener(_exitListener);
		}
	}

	@Override
	public boolean isInProgress()
	{
		return _state != EVENT_STATE.NOT_ACTIVE;
	}
}
